package cate.game.role.bag;

import cate.common.game.GameCollection;
import cate.common.table.d.GDBag;
import cate.common.table.d.GDHero;
import cate.common.table.d.GDObj;
import cate.common.table.item.ItemBaseRow;
import cate.common.util.GameResult;
import cate.game.res.MixRes;
import cate.game.res.MixResItem;
import cate.game.role.RolePart;
import cate.game.role.Role;
import cate.game.role.bag.awakenfairy.AwakenFairy;
import cate.game.role.bag.awakenfairy.AwakenFairyBag;
import cate.game.role.bag.equip.Equip;
import cate.game.role.bag.equip.EquipBag;
import cate.game.role.bag.fairy.Fairy;
import cate.game.role.bag.fairy.FairyBag;
import cate.game.role.bag.furniture.FurnitureBag;
import cate.game.role.bag.gem.GemBag;
import cate.game.role.bag.hero.Hero;
import cate.game.role.bag.hero.HeroBag;
import cate.game.role.bag.heropart.HeroPartBag;
import cate.game.role.bag.inscription.InscriptionBag;
import cate.game.role.bag.kitchenware.KitchenwareBag;
import cate.game.role.bag.num.NumResBag;
import cate.game.role.bag.prop.Prop;

import cate.game.role.bag.spice.SpiceBag;
import cate.game.role.bag.prop.PropBag;
import cate.game.role.bag.rune.RuneBag;
import cate.game.role.bag.storehouse.StoreHouse;
import easy.java.dev.note.NoteField;
import easy.java.practice.errorcode.EcResult;
import lombok.Getter;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.mapping.Document;


@Document(collection = GameCollection.NAME_ROLE_BAG)
public class RoleBag extends RolePart {
    public NumResBag numRes;
    public HeroBag hero;
    private EquipBag equip;
    public HeroPartBag heroPart;
    public PropBag prop;
    public RuneBag rune;
    public FurnitureBag furniture;
    public SpiceBag spice;
    public GemBag gem;
    private FairyBag fairy;
    @NoteField(value = "乾坤袋", detail = "仓库")
    public StoreHouse storeHouse;
    public InscriptionBag inscription;
    public AwakenFairyBag awakenFairy;
    public KitchenwareBag kitchenware;

    @Transient
    @Getter
    private transient MultiTypeBagProxy equipProxy;

    public RoleBag() {
    }

    public RoleBag(Role role) {
        super(role);
    }

    @Override
    public void initialize(Role role) {
        super.initialize(role);
        if (numRes == null) {
            numRes = new NumResBag(role);
        }
        numRes.initialize(role);
        if (hero == null) {
            hero = new HeroBag(role);
        }
        hero.initialize(role);
        if (heroPart == null) {
            heroPart = new HeroPartBag(role);
        }
        heroPart.initialize(role);
        if (prop == null) {
            prop = new PropBag(role);
        }
        prop.initialize(role);
        if (equip == null) {
            equip = new EquipBag(role);
        }
        equip.initialize(role);
        if (rune == null) {
            rune = new RuneBag(role);
        }
        rune.initialize(role);
        if (furniture == null) {
            furniture = new FurnitureBag(role);
        }
        furniture.initialize(role);
        if (spice == null) {
            spice = new SpiceBag(role);
        }
        spice.initialize(role);
        if (gem == null) {
            gem = new GemBag();
        }
        gem.initialize(role);
        if (fairy == null) {
            fairy = new FairyBag();
        }
        fairy.initialize(role);
        if (storeHouse == null) {
            storeHouse = new StoreHouse();
        }
        storeHouse.initialize(role);
        if (inscription == null) {
            inscription = new InscriptionBag(role);
        }
        inscription.initialize(role);
        if (awakenFairy == null) {
            awakenFairy = new AwakenFairyBag();
        }
        awakenFairy.initialize(role);
        if (kitchenware == null) {
            kitchenware = new KitchenwareBag();
        }
        kitchenware.initialize(role);

        equipProxy = new MultiTypeBagProxy("装备", GDBag.Capcity.EQUIP, equip, fairy, awakenFairy);
    }

    public BagContainerReference<EquipBag, Equip> getEquip() {
        return new BagContainerReference(EquipBag.class, Equip.class, equipProxy);
    }

    public BagContainerReference<FairyBag, Fairy> getFairy() {
        return new BagContainerReference(FairyBag.class, Fairy.class, equipProxy);
    }

    public BagContainerReference<AwakenFairyBag, AwakenFairy> getAwakenFairy() {
        return new BagContainerReference(AwakenFairyBag.class, AwakenFairy.class, equipProxy);
    }

    public IBagContainer getBag(int type) {
        if (type == GDObj.Type.NUM) {
            return numRes;
        } else if (type == GDObj.Type.PROP) {
            return prop;
        } else if (type == GDObj.Type.HERO) {
            return hero;
        } else if (type == GDObj.Type.HERO_PART) {
            return heroPart;
        } else if (type == GDObj.Type.EQUIP) {
            return getEquip();
        } else if (type == GDObj.Type.RUNE) {
            return rune;
        } else if (type == GDObj.Type.FURNITURE) {
            return furniture;
        } else if (type == GDObj.Type.SPICE) {
            return spice;
        } else if (type == GDObj.Type.GEM) {
            return gem;
        } else if (type == GDObj.Type.FAIRY) {
            return getFairy();
        }else if (type == GDObj.Type.INSCRIPTION) {
            return inscription;
        } else if (type == GDObj.Type.AWAKEN_FAIRY) {
            return getAwakenFairy();
        } else if (type == GDObj.Type.KITCHENWARE) {
            return kitchenware;
        }
        return null;
    }

    /**
     * 增加容量
     */
    public void addCapacity(byte bagType) {
        if (bagType != GDObj.Type.HERO) {
            return;
        }
        IBagContainer<?> bag = getBag(bagType);
        GameResult<Void> r = bag.tryAddCapacity();
        role.getGame().notice.message(role, r.message);
    }

    /**
     * 根据tid使用物品
     */
    public void useByTid(byte objType, int tid, int num) {
        if (objType != GDObj.Type.PROP) {
            return;
        }
        ((PropBag) getBag(objType)).usePropByTid(tid, num);
    }

    /**
     * 根据idHex使用物品
     */
    public void useByUid(byte objType, String uid) {
        if (objType != GDObj.Type.PROP) {
            return;
        }
        ((PropBag) getBag(objType)).usePropByUid(uid);
    }

    /**
     * 玩家创建时
     */
    @Override
    public void onCreate() {
        for (MixResItem item : new MixRes(GDHero.DEFAULT_CREATE_REWARD_STR).items) {
            if (item.type == GDObj.Type.HERO) {
                hero.createHeroFirst(new Hero(role, item.tid));
            } else if (item.type == GDObj.Type.PROP) {
                prop.addItem(item.tid, item.num, null);
            }
        }
    }

    @Override
    public void onEnter() {
        spice.onEnter();
    }

    public void storeByTid(byte objType, int tid, int num) {
        if (objType != GDObj.Type.PROP) {
            return;
        }
        storePropByTid(tid, num);
    }

    private void storePropByTid(int tid, int num) {
        ItemBaseRow tpl = role.getGame().table.item.base.get(tid);
        if (tpl == null) {
            logger.warn("尝试存储不存在的道具 id={}", tid);
            return;
        }

        if (!tpl.storeAvailable) {
            role.getGame().notice.message(role, "该物品不能存入仓库");
            return;
        }

        if (num <= 0) {
            role.getGame().notice.message(role, "道具不足");
            return;
        }

        long availableNum = prop.count(tid);
        if (availableNum < num) {
            role.getGame().notice.message(role, "道具不足");
            return;
        }

        EcResult<Void> r = storeHouse.addItem(tid, num, null);
        if (!r.ok()) {
            role.getGame().notice.message(role, r.message);
            return;
        }
        prop.removeItem(tid, num, null);
    }

    public void storeByUid(byte objType, String uid) {
        if (objType != GDObj.Type.PROP) {
            return;
        }
        storePropByUid(uid);
    }

    private void storePropByUid(String uid) {
        Prop item = prop.getItem(uid);
        if (item == null) {
            logger.warn("尝试存储不存在的道具 uid={}", uid);
            return;
        }
        if (item.num <= 0) {
            role.getGame().notice.message(role, "道具不足");
            return;
        }
        if (!item.getTpl(role.getGame()).storeAvailable) {
            role.getGame().notice.message(role, "该物品不能存入仓库");
            return;
        }

        EcResult<Void> r = storeHouse.addItem(item.tid, item.num, null);
        if (!r.ok()) {
            role.getGame().notice.message(role, r.message);
            return;
        }
        role.getBag().prop.removeItem(item.uid, null);
    }

    public void takeOutByTid(byte objType, int tid, int num) {
        if (objType != GDObj.Type.PROP) {
            return;
        }
        takeOutPropByUid(tid, num);
    }

    private void takeOutPropByUid(int tid, int num) {
        ItemBaseRow tpl = role.getGame().table.item.base.get(tid);
        if (tpl == null) {
            logger.warn("尝试存储不存在的道具 id={}", tid);
            return;
        }
        if (num <= 0) {
            role.getGame().notice.message(role, "道具不足");
            return;
        }

        long availableNum = storeHouse.count(tid);
        if (availableNum < num) {
            role.getGame().notice.message(role, "道具不足");
            return;
        }

        EcResult<Void> r = prop.addItem(tid, num, null);
        if (!r.ok()) {
            role.getGame().notice.message(role, r.message);
            return;
        }
        storeHouse.removeItem(tid, num, null);
    }

    public void takeOutByUid(byte objType, String uid) {
        if (objType != GDObj.Type.PROP) {
            return;
        }
        takeOutPropByUid(uid);
    }

    private void takeOutPropByUid(String uid) {
        Prop item = storeHouse.getItem(uid);
        if (item == null) {
            logger.warn("尝试存储不存在的道具 uid={}", uid);
            return;
        }
        if (item.num <= 0) {
            role.getGame().notice.message(role, "道具不足");
            return;
        }

        EcResult<Void> r = prop.addItem(item.tid, item.num, null);
        if (!r.ok()) {
            role.getGame().notice.message(role, r.message);
            return;
        }
        storeHouse.removeItem(item.uid, null);
    }

    public void tick() {
        prop.tick();
        hero.tick();
    }
}
